package cbbx_sm.utils.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import cbbx_sm.parser.BoundingBox;
import cbbx_sm.probabilistic_model.Cluster;


public class BoundingBoxPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	
	private static final int clusterCenterCrossSize = 3;
	
	private String imageFile = "data/images/testImage.jpg";
	
	private float height = 377.0f; // FIXME: size of parent container!
	private float width = 550.0f; // FIXME: size of parent container!
	private float maxHeight = 480.0f;
    private float maxWidth = 708.0f;
    private float x_proportion, y_proportion;
    
    private Image background = null;
    private RescaleOp rop;
    
	private Set<BoundingBox> boxes; // Useful only for determine maxHeight and maxWidth
	private BoundingBox curBox;
	private List<Cluster> clusters;

	private boolean showAllBoxes;

	public BoundingBoxPanel(Set<BoundingBox> boxes, List<Cluster> clusters, boolean showAllBoxes){
		super();
		
//		height = this.getParent().getHeight();
//		width = this.getParent().getWidth();
		this.boxes = boxes;
		this.clusters = clusters;
		this.showAllBoxes = showAllBoxes;
		
		// Calculating maxWeight and maxHeight in base of maximum
		// coordinate of bounding boxes.
//	    for (BoundingBox b : boxes){
//	    	if (maxHeight < b.getY_ul()) maxHeight = b.getY_ul();
//	    	if (maxHeight < b.getY_br()) maxHeight = b.getY_br();
//	    	if (maxWidth < b.getX_br()) maxWidth = b.getX_br();
//	    	if (maxWidth < b.getX_ul()) maxWidth = b.getX_ul();
//	    }
	    
//	    x_proportion = width / maxWidth;
//	    y_proportion = height / maxHeight;
	    x_proportion = (width - 10) / maxWidth;
	    y_proportion = (height - 30) / maxHeight;
	    
	    // Creating background image: loading, scaling and processing...
	    BufferedImage original = null;
	    try {
	    	original = ImageIO.read(new File(imageFile));
	    } catch (IOException e) {
			e.printStackTrace();
		}
		
	    Image scaledImage = original.getScaledInstance((int)width - 5, (int)height - 25, Image.SCALE_FAST);
		BufferedImage temp = new BufferedImage((int) width - 5, (int) height - 25, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = (Graphics2D) temp.getGraphics();
		g.drawImage(scaledImage, 0, 0, null);
		
		/* Create a rescale filter op that makes the image 50% opaque */
	    float[] scales = { 1f, 1f, 1f, 0.5f };
	    float[] offsets = new float[4];
	    rop = new RescaleOp(scales, offsets, null);
	    
	    background = new BufferedImage((int) width, (int) height, BufferedImage.TYPE_INT_ARGB);
	    g = (Graphics2D) background.getGraphics();
	    g.drawImage(temp, rop, 0, 0);
	}
	
	public void paint(Graphics g) {
	    Graphics2D g2 = (Graphics2D)g;
	    g2.clearRect(0, 0, (int)width, (int)height);

	    // Painting the background
	    g2.drawImage(background, 0, 0, null);
	    
		// If there are no boxes, draw nothing...
	    if (boxes == null) return;
		
	    if (showAllBoxes){
	    	for (BoundingBox b : boxes){   	    		
	    		float x_br = b.getX_br() * (width - 10) / maxWidth;
	    		float x_ul = b.getX_ul() * (width - 10) / maxWidth;
	    		float y_br = b.getY_br() * (height - 30) / maxHeight;
	    		float y_ul = b.getY_ul() * (height - 30) / maxHeight;
	    		
	    		
	    		Rectangle2D rectangle = new Rectangle2D.Float(x_ul, y_ul, 
	    													  x_br - x_ul, y_br - y_ul);    		

	 		
	    		g2.draw(rectangle);
	    	}
	    }

	    
	    // Draw the current box
    	if (getCurBox() != null){
        	g2.setColor(Color.GREEN);

	    	float x_br = getCurBox().getX_br() * x_proportion;
    		float x_ul = getCurBox().getX_ul() * x_proportion;
    		float y_br = getCurBox().getY_br() * y_proportion;
    		float y_ul = getCurBox().getY_ul() * y_proportion;
    		
    		Rectangle2D rectangle = new Rectangle2D.Float(x_ul, y_ul, 
    													  x_br - x_ul, y_br - y_ul);
    		
    		g2.draw(rectangle);
	    }
    	
    	if (clusters == null) return;
    	
    	// Drawing a cross for each cluster center and 
    	// the cluster shape (now a circle)
    	for(Cluster c : clusters){	
    		
    		//if (getCurBox() != null && c.contains(box))
    		double clusterx = c.getX() * x_proportion;
    		double clustery = c.getY() * y_proportion;
    		double radiusx = c.getRadius() * x_proportion;
    		double radiusy = c.getRadius() * y_proportion;
    		
    		// Painting the center of the cluster
        	g2.setColor(Color.RED);
        	g2.setStroke(new BasicStroke(1.5f));
        	
    		Point2D.Double point1 = new Point2D.Double(clusterx - clusterCenterCrossSize, clustery - clusterCenterCrossSize);
    		Point2D.Double point2 = new Point2D.Double(clusterx + clusterCenterCrossSize, clustery + clusterCenterCrossSize);
    		g2.draw(new Line2D.Double(point1, point2));
    		
    		point1 = new Point2D.Double(clusterx + clusterCenterCrossSize, clustery - clusterCenterCrossSize);
    		point2 = new Point2D.Double(clusterx - clusterCenterCrossSize, clustery + clusterCenterCrossSize);
    		g2.draw(new Line2D.Double(point1, point2));
    		
    		// Painting the shape of the cluster
    		g2.setColor(Color.BLUE);
        	g2.setStroke(new BasicStroke(1.0f));
        	
        	Ellipse2D.Double circle = new Ellipse2D.Double(clusterx - radiusx, clustery - radiusy, 2.0 * radiusx, 2.0 * radiusy);
        	g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL, 0.0f, new float[] {15.0f, 6.0f, 2.5f, 6.0f}, 0.0f));
        	g2.draw(circle);
    	}
	}
	

	public Set<BoundingBox> getBoxes() {
		return boxes;
	}

	public void setBoxes(Set<BoundingBox> boxes) {
		this.boxes = boxes;
	}

	public void setCurBox(BoundingBox curBox) {
		this.curBox = curBox;
	}

	private BoundingBox getCurBox() {
		return curBox;
	}
}
